home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_pas / crti / crti.pas
Pascal/Delphi Source File  |  1988-03-03  |  18KB  |  350 lines

  1. UNIT CRTi; {version 1.00 of 03/03/88}
  2.  
  3. {Copyright (c) 1988 by Carley Phillips.  Placed in the PUBLIC DOMAIN.}
  4.  
  5. {
  6. Many of the message threads on Compuserve's BPROGA DL 2 involve the use (and
  7. mis-use) of the CRT unit provided with Turbo 4.  These routines were written
  8. to take some of the mystery out of what CRT routines do and do not do.
  9.  
  10. This unit is an input-only replacement for Borland's CRT.  This means that:
  11. 1. only the input and the misc functions (e.g. Sound) are implemented;
  12. 2. the parts which have to do with screen output are omitted; and
  13. 3. you have access to the source so you can change what you don't like.
  14.  
  15. This allows your programs to avoid USEing CRT at all if your video output
  16. is done with Qwik40, FastWrite, or other packages.  If you use CRTi anywhere,
  17. then make sure none of your units use CRT because the conflicts which may
  18. result may be very difficult to debug.
  19.  
  20. Note that the equivalent of a Turbo variable (which they should have made
  21. public) is contained in HadControlBreak.  This is the variable set by the
  22. control-break handler if CheckBreak is true.  Normally Turbo tests this
  23. during ReadKey and during screen output.  Your program can test this at
  24. any time, however, such as in your replacement screen routines or during
  25. a long mathematical calculation.
  26.  
  27. There are two new public routines here:
  28. 1. a keyboard flushing routine; and
  29. 2. an abort routine you can call if your program finds HadControlBreak true.
  30.  
  31. I will update these routines if anyone notifies me of bugs or of any way in
  32. which these routines do not provide satisfactory duplicates of Borland
  33. functionality.
  34.  
  35. Comments, suggestions, bug reports, etc. should be sent on Compuserve (via
  36. EasyPlex since I'm not necessarily on every few days) to
  37.  
  38. Carley Phillips, 76630,3312.
  39. }
  40.  
  41. {*****************************************************************************}
  42. INTERFACE
  43.  
  44. var
  45.    CheckBreak      : boolean; {false means ignore ^Break}
  46.    SaveInt1B       : pointer; {where old interrupt 1B address is saved}
  47.    HadControlBreak : boolean; {set by ^Break handler if CheckBreak true}
  48.  
  49. procedure Delay (mSec : word);
  50. {
  51. Direct replacement for CRT.Delay.  Delays mSec number of milliseconds.
  52.  
  53. Note that, like Borland's CRT unit, a delay count is determined during
  54. initialization.  This means that if you change your machine speed after
  55. starting a program, the delay values will no longer be what you intended them
  56. to be.
  57. }
  58.  
  59. procedure Sound (Hz : word);
  60. {
  61. Direct replacement for CRT.Sound.  Starts a Hz frequency tone until it is
  62. turned off by NoSound.
  63. }
  64.  
  65. procedure NoSound;
  66. {
  67. Direct replacement for CRT.NoSound.  Stops a tone started with Sound.
  68. }
  69.  
  70. function Keypressed : boolean;
  71. {
  72. Direct replacement for CRT.Keypressed.  If you like, you can also check for
  73. a control break here by un-commenting the indicated lines.
  74. }
  75.  
  76. function ReadKey : char;
  77. {
  78. Direct replacement for CRT.ReadKey.
  79. }
  80.  
  81. procedure FlushKeyboard;
  82. {
  83. NEW public routine to flush the BIOS keyboard buffer.  That is, unless the
  84. user is extraordinarily lucky with his timing of new input, Keypressed will
  85. be false immediatedly following FlushKeyboard.  Normally one flushes the
  86. keyboard buffer in situations such as displaying an error message and then
  87. waiting on the user to press a key.  If the buffer is not flushed, then the
  88. user may have typed ahead enough that the error message will be on the screen
  89. for too short a time to be read.
  90. }
  91.  
  92. procedure ControlBreakAbort;
  93. {
  94. NEW public abort routine called when ReadKey detects that an allowed ^Break has
  95. been entered.  Borland does something similar, but you are free to replace this
  96. with other code or even an external routine.  Alternatively, you can use this
  97. routine from your code if you find that HadControlBreak has become true.
  98. }
  99.  
  100. {*****************************************************************************}
  101. IMPLEMENTATION
  102.  
  103. var
  104.    Cpm : word;   {counts per msec; initialized by InitDelay; used by Delay}
  105.    Scan: byte;   {saves scan code for Keypressed and ReadKey}
  106.    ExitSave : pointer; {place to save old ExitProc}
  107.  
  108. {*****************************************************************************}
  109. procedure Delay (mSec : word);
  110. begin
  111. Inline(                  {Assembly by Inline 03/03/88 22:28}
  112.   $8B/$56/<MSEC/         {         mov    DX,[BP+<mSec];get number of msec}
  113.   $09/$D2/               {         or     DX,DX       ;check it}
  114.   $74/$17/               {         jz     quit        ;quit if 0 delay}
  115.   $BF/>CPM/              {         mov    DI,>Cpm     ;setup addr for subr}
  116.   $8B/$05/               {         mov    AX,[DI]     ;delay count for 1msec}
  117.   $89/$C1/               {onemsec: mov    CX,AX       ;init delay subr counter}
  118.   $E8/$06/$00/           {         call   subr        ;call delay subr}
  119.   $4A/                   {         dec    DX          ;count that we've done 1}
  120.   $75/$F8/               {         jnz    onemsec     ;loop for each msec}
  121.   $E9/$07/$00/           {         jmp    quit        ;skip over subroutine}
  122.   $3A/$05/               {subr:    cmp    AL,[DI]     ;check if constant chngd}
  123.   $75/$02/               {         jnz    out         ;it never will change}
  124.   $E2/$FA/               {         loop   subr        ;count and loop for msec}
  125.   $C3                    {out:     ret                ;return from local subr}
  126.  );                      {quit:                       ;label for end of inline}
  127. end;
  128.  
  129. {*****************************************************************************}
  130. procedure Sound (Hz : word);
  131. begin
  132. Inline(                  {Assembly by Inline 03/03/88 22:28}
  133.   $8B/$4E/<HZ/           {         mov    CX,[BP+<Hz] ;get frequency}
  134.   $B8/$DD/$34/           {         mov    AX,$34DD    ;load lsh of 1,193,181}
  135.   $BA/$12/$00/           {         mov    DX,$0012    ;load msh of 1,193,181}
  136.   $39/$CA/               {         cmp    DX,CX       ;check frequency}
  137.   $73/$1A/               {         jnb    quit        ;quit if too small}
  138.   $F7/$F1/               {         div    CX          ;calculate timer divider}
  139.   $89/$C1/               {         mov    CX,AX       ;save quotient}
  140.   $E4/$61/               {         in     AL,$61      ;get sense data}
  141.   $A8/$03/               {         test   AL,$03      ;check for two lsb}
  142.   $75/$08/               {         jnz    set         ;skip if on}
  143.   $0C/$03/               {         or     AL,$03      ;turn on two lsb}
  144.   $E6/$61/               {         out    $61,AL      ;turn them on}
  145.   $B0/$B6/               {         mov    AL,$B6      ;load control value}
  146.   $E6/$43/               {         out    $43,AL      ;output to timer control}
  147.   $88/$C8/               {set:     mov    AL,CL       ;get lsh of quotient}
  148.   $E6/$42/               {         out    $42,AL      ;output to timer2}
  149.   $88/$E8/               {         mov    AL,CH       ;get msh of quotient}
  150.   $E6/$42                {         out    $42,AL      ;output to timer2}
  151.  );                      {quit:                       ;label for end of inline}
  152. end;
  153.  
  154. {*****************************************************************************}
  155. procedure NoSound;
  156. begin
  157. Inline(                  {Assembly by Inline 03/03/88 22:28}
  158.   $E4/$61/               {         in     AL,$61      ;get sense data}
  159.   $24/$FC/               {         and    AL,$FC      ;turn off two lsb}
  160.   $E6/$61                {         out    $61,AL      ;put it back}
  161.  );                      {quit:                       ;label for end of inline}
  162. end;
  163.  
  164. {*****************************************************************************}
  165. function KeyPressed : boolean;
  166. begin
  167. (*
  168.    if HadControlBreak then   {un-comment this if you want to check ^Break here}
  169.       ControlBreakAbort;
  170. *)
  171. Inline(                  {Assembly by Inline 03/03/88 22:28}
  172.   $80/$3E/>SCAN/$00/     {         cmp    Byte Ptr[>Scan],$00;check scan code}
  173.   $75/$08/               {         jnz    yes         ;not 0 = we saved one}
  174.   $B4/$01/               {         mov    AH,$01      ;check for char}
  175.   $CD/$16/               {         int    $16         ;using BIOS}
  176.   $B0/$00/               {         mov    AL,$00      ;assume we had none}
  177.   $74/$02/               {         jz     quit        ;Z flag means we didn't}
  178.   $B0/$01/               {yes:     mov    AL,$01      ;load "true"}
  179.   $88/$46/$FF);          {quit:    mov    [BP-01],AL  ;save final value}
  180. end;
  181.  
  182. {*****************************************************************************}
  183. function ReadKey : char;
  184. begin
  185. Inline(                  {Assembly by Inline 03/03/88 22:28}
  186.   $B0/$00/               {         mov    AL,0        ;load a zero}
  187.   $86/$06/>SCAN/         {         xchg   [>Scan],AL  ;xchg with scan code}
  188.   $08/$C0/               {         or     AL,AL       ;check for saved code}
  189.   $75/$12/               {         jnz    saveit      ;not 0 = we had one}
  190.   $30/$E4/               {         xor    AH,AH       ;read char}
  191.   $CD/$16/               {         int    $16         ;using BIOS}
  192.   $08/$C0/               {         or     AL,AL       ;check "char" we got}
  193.   $75/$0A/               {         jnz    saveit      ;not 0 = got normal char}
  194.   $88/$26/>SCAN/         {         mov    [>Scan],AH  ;save extended scan code}
  195.   $08/$E4/               {         or     AH,AH       ;check scan code}
  196.   $75/$02/               {         jnz    saveit      ;not = 0 means not ^Brk}
  197.   $B0/$03/               {         mov    al,$03      ;use "normal" ^C instead}
  198.   $88/$46/$FF);          {saveit:  mov    [BP-1],AL   ;save final char}
  199.    if HadControlBreak then
  200.       ControlBreakAbort;
  201. end;
  202.  
  203. {*****************************************************************************}
  204. procedure FlushKeyboard;
  205. begin
  206. Inline(                  {Assembly by Inline 03/03/88 22:28}
  207.   $B4/$01/               {flush:   mov    AH,$01      ;check for char}
  208.   $CD/$16/               {         int    $16         ;using BIOS}
  209.   $74/$06/               {         jz     empty       ;Z flag means none there}
  210.   $30/$E4/               {         xor    AH,AH       ;read char}
  211.   $CD/$16/               {         int    $16         ;using BIOS}
  212.   $EB/$F4/               {         jmp    flush       ;loop until no more}
  213.   $C6/$06/>SCAN/$00);    {empty:   mov    Byte Ptr [>Scan],0;clear saved code}
  214. end;
  215.  
  216. {*****************************************************************************}
  217. procedure ControlBreakAbort;
  218. begin
  219.    HadControlBreak := false;
  220.    FlushKeyboard;
  221. Inline(                  {Assembly by Inline 03/03/88 22:28}
  222.   $BB/$07/$00/           {         mov    BX,$0007    ;pg 0; graphics fg color}
  223.   $B8/$07/$0E/           {         mov    AX,$0E07    ;load a BEL}
  224.   $CD/$10/               {         int    $10         ;output using BIOS}
  225.   $B8/$5E/$0E/           {         mov    AX,$0E5E    ;load a '^'}
  226.   $CD/$10/               {         int    $10         ;output using BIOS}
  227.   $B8/$43/$0E/           {         mov    AX,$0E43    ;load a 'C'}
  228.   $CD/$10/               {         int    $10         ;output using BIOS}
  229.   $B8/$0D/$0E/           {         mov    AX,$0E0D    ;load a CR}
  230.   $CD/$10/               {         int    $10         ;output using BIOS}
  231.   $B8/$0A/$0E/           {         mov    AX,$0E0A    ;load a LF}
  232.   $CD/$10/               {         int    $10         ;output using BIOS}
  233.   $CD/$23                {         int    $23         ;control-C interrupt}
  234.                          {;Note that DOS manual says only DOS should do this.}
  235.                          {;However, Turbo issues this interrupt directly in}
  236.                          {;these same circumstances.  It is probably ok since}
  237.  );                      {;Turbo has hooked this interrupt.}
  238. end;
  239.  
  240. {*****************************************************************************}
  241. {this is not public}
  242. procedure InitDelay;
  243. {
  244. Right after loading, this routine executes, for 1 timer tick (55 msec) a delay
  245. subroutine which is identical to that contained in the public Delay routine.
  246. From the count generated, it is possible to calculate what the count would have
  247. been for 1 millisecond and save this count in Cpm for later use by Delay.
  248. Note that, like Borland's CRT unit, this count is determined during
  249. initialization.  This means that if you change your machine speed after
  250. starting a program, the delay values will no longer be what you intended them
  251. to be.
  252. }
  253. begin
  254. Inline(                  {Assembly by Inline 03/03/88 22:28}
  255.   $1E/                   {         push   DS          ;save DS}
  256.   $B8/$40/$00/           {         mov    AX,$0040    ;BIOS data segment}
  257.   $8E/$D8/               {         mov    DS,AX       ;into DS}
  258.   $BF/$6C/$00/           {         mov    DI,$6C      ;offset for lsh of timer}
  259.   $8A/$05/               {         mov    AL,[DI]     ;get lsb of timer}
  260.   $3A/$05/               {sync:    cmp    AL,[DI]     ;check if timer changed}
  261.   $74/$FC/               {         jz     sync        ;loop until it changes}
  262.   $8A/$05/               {         mov    AL,[DI]     ;get new timer value}
  263.   $B9/$FF/$FF/           {         mov    CX,$FFFF    ;initialize large count}
  264.   $E8/$12/$00/           {         call   subr        ;call delay subr}
  265.   $1F/                   {         pop    DS          ;restore DS}
  266.   $89/$C8/               {         mov    AX,CX       ;count to lsh of dividnd}
  267.   $F7/$D0/               {         not    AX          ;AX=count for 55 msec}
  268.   $31/$D2/               {         xor    DX,DX       ;clear msh of dividend}
  269.   $B9/$37/$00/           {         mov    CX,55       ;msec per timer tick}
  270.   $F7/$F1/               {         div    CX          ;compute count for 1 msec}
  271.   $A3/>CPM/              {         mov    [>Cpm],AX   ;save count for Delay}
  272.   $E9/$07/$00/           {         jmp    quit        ;skip over subroutine}
  273.   $3A/$05/               {subr:    cmp    AL,[DI]     ;check if timer changed}
  274.   $75/$02/               {         jnz    out         ;quit after 1 timer tick}
  275.   $E2/$FA/               {         loop   subr        ;count and loop}
  276.   $C3                    {out:     ret                ;return from local subr}
  277.  );                      {quit:                       ;label for end of inline}
  278. end;
  279.  
  280. {*****************************************************************************}
  281. {this is not public}
  282. procedure ControlBreakHandler (Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP: word);
  283. interrupt;
  284. {
  285. Initialization code sets this up as the interrupt handler for a $1B (control-
  286. break interrupt.  All it does is set a flag for checking by other routines.
  287. }
  288. begin
  289.    HadControlBreak := CheckBreak;
  290. end;
  291.  
  292. {*****************************************************************************}
  293. {this is not public}
  294. procedure InitControlBreak;
  295. {
  296. Initialization routine to install ControlBreakHandler as the interrupt $1B
  297. handler.  The old vector is saved in the public SaveInt1B just as in CRT.
  298. This could be done totally in Turbo (without inline) but would require this
  299. unit to USE the DOS unit.
  300. }
  301. var
  302.    OurAdr : pointer;
  303. begin
  304.    OurAdr := @ControlBreakHandler;
  305. Inline(                  {Assembly by Inline 03/03/88 22:28}
  306.   $B8/$1B/$35/           {         mov    AX,$351B    ;setup for interrupt 1B}
  307.   $CD/$21/               {         int    $21         ;get old int vector}
  308.   $89/$1E/>SAVEINT1B/    {         mov    [>SaveInt1B],BX;save offset}
  309.   $8C/$C3/               {         mov    BX,ES       ;get segment}
  310.   $89/$1E/>SAVEINT1B+2/  {         mov    [>SaveInt1B+2],BX;save segment}
  311.   $8B/$56/<OURADR/       {         mov    DX,[BP+<OurAdr]  ;get our offset}
  312.   $8B/$46/<OURADR+2/     {         mov    AX,[BP+<OurAdr+2];get our segment}
  313.   $1E/                   {         push   DS          ;save DS}
  314.   $8E/$D8/               {         mov    DS,AX       ;move segment for call}
  315.   $B8/$1B/$25/           {         mov    AX,$251B    ;setup for interrupt 1B}
  316.   $CD/$21/               {         int    $21         ;set new int vector}
  317.   $1F);                  {         pop    DS          ;restore DS}
  318. end;
  319.  
  320. {*****************************************************************************}
  321. {this is not public}
  322. {$F+} procedure ExitHandler; {$F-}
  323. {
  324. Exit procedure to de-install ControlBreakHandler.
  325. This could be done totally in Turbo (without inline) but would require this
  326. unit to USE the DOS unit.
  327. }
  328. begin
  329. Inline(                  {Assembly by Inline 03/03/88 22:28}
  330.   $8B/$16/>SAVEINT1B/    {         mov    DX,[>SaveInt1B];get orig offset}
  331.   $A1/>SAVEINT1B+2/      {         mov    AX,[>SaveInt1B+2];get orig segment}
  332.   $1E/                   {         push   DS          ;save DS}
  333.   $8E/$D8/               {         mov    DS,AX       ;move segment for call}
  334.   $B8/$1B/$25/           {         mov    AX,$251B    ;setup for interrupt 1B}
  335.   $CD/$21/               {         int    $21         ;restore old int vector}
  336.   $1F);                  {         pop    DS          ;restore DS}
  337.    ExitProc := ExitSave; {when we leave, chain to next exit procedure}
  338. end;
  339.  
  340. {*****************************************************************************}
  341. begin {CRTi initialization}
  342.    CheckBreak := true;            {default break-checking flag to True}
  343.    Scan := 0;                     {initialize saved scan code}
  344.    HadControlBreak := false;      {initialize flag which says we had break}
  345.    InitDelay;                     {determine the proper count for use by Delay}
  346.    InitControlBreak;              {set up control-break handler}
  347.    ExitSave := ExitProc;          {save old exit procedure}
  348.    ExitProc := @ExitHandler;      {put our exit procedure in the chain}
  349. end. {CRTi initialization}
  350.